命令式 vs 声明式
框架设计的第一个权衡维度:代码编写方式。
命令式(Imperative)
直接操作 DOM,关注"怎么做":
// jQuery 风格
$('#app').text('new content')
javascript
声明式(Declarative)
描述期望的结果,框架负责实现:
<!-- Vue 模板 -->
<div>{{ message }}</div>
html
对比分析
| 维度 | 命令式 | 声明式 |
|---|---|---|
| 心智负担 | 高,需手动管理每一步操作 | 低,只需描述最终状态 |
| 可维护性 | 差,嵌套深时代码难以维护 | 好,模板结构直观 |
| 性能上限 | 高,直接操作 DOM 无中间层 | 低,需经过框架解析和 Diff |
核心矛盾:声明式代码的性能不优于命令式代码。声明式需要额外的编译或解析过程(模板编译为渲染函数、Diff 算法对比新旧 vnode),这些都会产生运行时开销。
Virtual DOM 的性能定位
Virtual DOM 的出现是为了在声明式的开发体验和命令式的性能之间找到平衡点。
三种更新策略对比
| 策略 | JS 计算开销 | DOM 操作开销 | 性能影响因素 |
|---|---|---|---|
| 原生 JS | 无 | 直接操作 | DOM 嵌套深度 |
| Virtual DOM | 创建响应式对象 + Diff | 只更新差异节点 | 数据变化量 |
| innerHTML | 拼接 HTML 字符串 | 销毁全部 + 重建全部 | 模板大小 |
JS 层面的计算(创建对象、Diff)比 DOM 操作快一个数量级。当数据变化少时,Virtual DOM 只需做少量 DOM 更新,性能远优于 innerHTML 的"全部推倒重建"。
基准测试数据参考
在 JSBench 测试中,纯 JS 数组操作(不涉及 DOM)可达约 12000 ops/s,而通过 document.createElement 创建真实 DOM 节点仅约 109 ops/s,差距超过 99%。
运行时 vs 编译时
框架设计的第二个权衡维度:工作阶段。
纯运行时框架
用户直接提供渲染函数所需的数据结构对象,框架在运行时将其渲染为 DOM。没有编译过程,无法进行编译期优化。
纯编译时框架
将用户编写的模板直接编译为原生 DOM 操作代码(如 document.createElement、document.appendChild)。性能最优(无运行时开销),但灵活性受限——无法在运行时动态生成模板。
运行时 + 编译时(Vue/React 的选择)
Vue 和 React 都采用运行时 + 编译时的混合方案:
| 阶段 | 做什么 | 好处 |
|---|---|---|
| 编译时 | 模板编译为渲染函数;静态分析优化 | 提前完成可预知的优化 |
| 运行时 | 响应式追踪、vnode Diff、DOM 更新 | 支持动态数据和交互 |
这种方案的优势在于:编译时可以进行静态标记(如 Vue 3 的 PatchFlag),运行时只需处理动态部分,两者配合实现最优性能。
总结
框架设计是性能与开发体验之间的权衡艺术:
- 声明式牺牲了少量性能上限,换取更好的可维护性和更低的认知负担
- Virtual DOM 在声明式框架中平衡了更新性能
- 运行时 + 编译时的混合方案兼顾了灵活性和编译期优化空间
Vue 3 在这个设计空间中做出了自己的选择:通过编译时优化(静态提升、PatchFlag、Block Tree)尽量缩小声明式与命令式之间的性能差距。
↑